﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.Linq;
using ScrumTable.Common;
using ScrumTable.Common.Collections;
using ScrumTable.Common.Data.Tracking;
using CacheIterationBucket = ScrumTable.DL.Data.Generic.Cache.CacheElementListEntryBucket<ScrumTable.DL.Data.Generic.Cache.CacheIteration>;
using CacheWorkItemBucket = ScrumTable.DL.Data.Generic.Cache.CacheElementListEntryBucket<ScrumTable.DL.Data.Generic.Cache.CacheWorkItem>;

using CacheIterationSavePair = ScrumTable.DL.Data.Generic.Cache.CacheSavePair<ScrumTable.DL.Data.Generic.Cache.CacheElementListEntryBucket<ScrumTable.DL.Data.Generic.Cache.CacheIteration>>;
using CacheWorkItemSavePair = ScrumTable.DL.Data.Generic.Cache.CacheSavePair<ScrumTable.DL.Data.Generic.Cache.CacheElementListEntryBucket<ScrumTable.DL.Data.Generic.Cache.CacheWorkItem>>;

#endregion

namespace ScrumTable.DL.Data.Generic.Cache
{
    /// <summary>
    /// Contains the changes which were made on the internal data cache.
    /// </summary>
    public class CacheDataChanges
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Returns/Sets the last save time of the data changes.
        /// </summary>
        public DateTime LastSaveTime { get; set; }

        /// <summary>
        /// Gets the project which accommodates the changes.
        /// </summary>
        /// <remarks>
        /// This property contains the ORIGINAL, NOT THREAD SAFE project instance.
        /// 
        /// !!!  DO NOT MODIFY OR CHANGE CONTENT OF THIS OBJECT IN   !!!
        /// !!!  THE PARALLEL SAVE THREAD.                           !!!
        /// </remarks>
        public CacheProject Project { get; private set; }

        /// <summary>
        /// Returns true if all elements have been accepted.
        /// </summary>
        public bool Accepted
        {
            get
            {
                KeyValuePair<string, CacheWorkItemSavePair> notAcceptedWi = WorkItemsToSync.FirstOrDefault(pair => !pair.Value.Accepted);

                if (!Equals(default(KeyValuePair<string, CacheWorkItemSavePair>), notAcceptedWi))
                    return false;

                KeyValuePair<string, CacheIterationSavePair> notAcceptedIt = IterationsToSync.FirstOrDefault(pair => !pair.Value.Accepted);

                if (!Equals(default(KeyValuePair<string, CacheIterationSavePair>), notAcceptedIt))
                    return false;

                return true;
            }
        }

        /// <summary>
        /// Gets the changed work items without hierarchy information.
        ///  Key:   Unique id chain (parent unique id -> parent unique id -> WorkItem id) of the WorkItem
        ///  Value: Bucket with the information about the changes and the WorkItem
        /// </summary>
        /// <remarks>
        /// This dictionary is readonly. Use WorkItemsToSync to add new workitems to
        /// DataLayer.
        /// </remarks>
        public IDictionary<ValueChain<string>, CacheWorkItemBucket> ChangedWorkItems
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the changed iterations. The list is build up by:
        ///  Key:   Unique id of the Iteration
        ///  Value: Bucket with the information about the changes and the Iteration
        /// </summary>
        /// <remarks>
        /// This dictionary is readonly. Use IterationsToSync to add new workitems to
        /// DataLayer.
        /// </remarks>
        public IDictionary<string, CacheIterationBucket> ChangedIterations
        {
            get;
            private set;
        }

        /// <summary>
        /// Specifies the list with all work item to synchronize back into the
        /// generic data layer.
        ///  Key:   Unique id of the Iteration
        ///  Value: Bucket with the information about the changes and the Iteration
        /// </summary>
        public IDictionary<string, CacheIterationSavePair> IterationsToSync { get; private set; }

        /// <summary>
        /// Gets all cache identities stored in the current cache element set.
        /// </summary>
        public CacheIdentityStore Identities { get; private set; }

        /// <summary>
        /// Gets all cache identities stored in the current cache element set.
        /// </summary>
        public IdGenerator IdentitiesIdGenerator { get; private set; }

        /// <summary>
        /// Specifies the list with all work item to synchronize back into the
        /// generic data layer.
        ///  Key:   Unique id of the WorkItem
        ///  Value: Bucket with the information about the changes and the WorkItem
        /// </summary>
        public IDictionary<string, CacheWorkItemSavePair> WorkItemsToSync { get; private set; }

        /// <summary>
        /// Gets the number of changed items.
        /// </summary>
        public int ChangedElementCount
        {
            get { return (ChangedWorkItems.Count + ChangedIterations.Count); }
        }

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        internal CacheDataChanges(
            CacheProject project,
            CacheElementChangeSet<string, CacheIteration> changedIterations,
            CacheElementChangeSet<ValueChain<string>, CacheWorkItem> changedWorkItems,
            CacheIdentityStore allIdentities)
        {
            PreCondition.AssertNotNull(project, "project");
            PreCondition.AssertNotNull(changedIterations, "changedIterations");
            PreCondition.AssertNotNull(changedWorkItems, "changedWorkItems");
            PreCondition.AssertNotNull(allIdentities, "allIdentities");

            ChangedWorkItems = new ReadOnlyDictionary<ValueChain<string>, CacheWorkItemBucket>(changedWorkItems);
            ChangedIterations = new ReadOnlyDictionary<string, CacheIterationBucket>(changedIterations);
            Project = project;
            LastSaveTime = project.LastSaveTime;
            Identities = allIdentities;
            IdentitiesIdGenerator = project.OwnerContext.ElementFactory.IdGenerator;

            // create save pairs from changed WorkItems
            WorkItemsToSync = ChangedWorkItems.Convert(
                pair =>
                    {
                        return new KeyValuePair<string, CacheWorkItemSavePair>(pair.Key.Top, new CacheWorkItemSavePair(pair.Value));
                    });

            // create save pairs from changed Iterations
            IterationsToSync = ChangedIterations.Convert(
                pair =>
                    {
                        return new KeyValuePair<string, CacheIterationSavePair>(pair.Key, new CacheIterationSavePair(pair.Value));
                    });
        }

        internal CacheDataChanges()
        {
            IterationsToSync = new Dictionary<string, CacheIterationSavePair>();
            WorkItemsToSync = new Dictionary<string, CacheWorkItemSavePair>();

            Identities = new CacheIdentityStore();
            ChangedWorkItems = new ReadOnlyDictionary<ValueChain<string>, CacheWorkItemBucket>();
            ChangedIterations = new ReadOnlyDictionary<string, CacheIterationBucket>();
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Removes the dirty flags on all elements. This feature can be used
        /// on back end system which don't recreate the synchronization tree.
        /// </summary>
        public void RemoveDirtyFlags()
        {
            WorkItemsToSync.ForEach(wi =>
            {
                wi.Value.Bucket.RemoveDirtyFlag();
                wi.Value.Bucket.Element.RemoveDirtyFlag();
            });

            IterationsToSync.ForEach(it =>
            {
                it.Value.Bucket.RemoveDirtyFlag();
                it.Value.Bucket.Element.RemoveDirtyFlag();
            } );
        }

        /// <summary>
        /// Inserts the specified exception into all elements to synchronize.
        /// So the layers above are able to associated the error to the corresponding
        /// elements.
        /// </summary>
        /// <param name="e">Specifies the error to assign.</param>
        public void MarkChangesAsError(Exception e)
        {
            WorkItemsToSync.ForEach(wi =>
            {
                wi.Value.State = CacheSavePairState.Error;

                if (e != null)
                {
                    wi.Value.Errors.Add(new CacheSaveError(e));
                }
            });

            IterationsToSync.ForEach(wi =>
            {
                wi.Value.State = CacheSavePairState.Error;

                if (e != null)
                {
                    wi.Value.Errors.Add(new CacheSaveError(e));
                }
            });
        }

        /// <summary>
        /// Sets all changes to the given state.
        /// </summary>
        /// <param name="state">Specifies the state to write into the CacheSavePairs.</param>
        public void MarkChangesAs(CacheSavePairState state)
        {
            WorkItemsToSync.ForEach(wi => wi.Value.State = state);
            IterationsToSync.ForEach(it => it.Value.State = state);
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}